/**
 * 
 */
package ca.csf.dfc.poo.iu;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.xml.stream.XMLStreamException;

import ca.csf.dfc.poo.bots.Robot;
import ca.csf.dfc.poo.bots.RobotGreedy;
import ca.csf.dfc.poo.bots.RobotIntelligent;
import ca.csf.dfc.poo.bots.RobotRandom;
import ca.csf.dfc.poo.db.DbRecompenses;
import ca.csf.dfc.poo.eng.Engine;
import ca.csf.dfc.poo.env.Position;

/**
 * Classe représentant l'interface principale où se déroule la simulation.
 * 
 * @author Jean-François Bédard, Marie-Pier Chabot, 
 * Alexandre Paquet Fasys, Claude Roy
 */
public class IntPrincipale extends JFrame
{
	private static final long serialVersionUID = 1L;
	
	//Création des items qui apparaîtront dans le sous-menu "Fichier" du menu principal
	/**
	 * Item «Charger» du menu
	 */
	private JMenuItem itm_Charger = new JMenuItem("Charger un environnement");
	/**
	 * Item «Quitter» du menu
	 */
	private JMenuItem itm_Quitter = new JMenuItem("Quitter");
	
	/**
	 * Création d'une étiquette pour les erreurs de base de données
	 */
	private JLabel lbl_ErreurSQL = new JLabel("");
	
	//Création des boutons qui apparaîtront à droite de l'interface
	/**
	 * Bouton pour charger un environnement
	 */
	private JButton btn_Charger = new JButton("Charger un environnement");
	/**
	 * Bouton pour afficher la mémoire interne des robots intelligents
	 */
	private JButton btn_MemoireInterne = new JButton("Memoire interne");
	/**
	 * Bouton pour ajouter des robots dans un environnement
	 */
	private JButton btn_AjoutRobot = new JButton("Ajouter un/des robot(s)");
	/**
	 * Bouton pour afficher les meilleurs scores des types de robots pour un environnement
	 */
	private JButton btn_MeilleursPoint = new JButton("Afficher les meilleurs pointages");
	
	//Création des boutons qui apparaîtront en bas de l'interface
	/**
	 * Bouton pour ralentir la vitesse de simulation
	 */
	private JButton btn_Ralentir = new JButton ("Ralentir");
	/**
	 * Bouton pour commencer/poursuivre la simulation
	 */
	private JButton btn_Play = new JButton("Play");
	/**
	 * Bouton pour stopper la simulation
	 */
	private JButton btn_Pause = new JButton("Pause");
	/**
	 * Bouton pour accélérer la vitesse de simulation
	 */
	private JButton btn_Accelerer = new JButton("Accelerer");
	/**
	 * Bouton pour quitter l'interface de simulation
	 */
	private JButton btn_Quitter = new JButton("Retour a la page d'accueil");
	
	//Création des étiquettes de la légende
	/**
	 * Étiquette pour la case de plancher
	 */
	private JLabel lbl_CasePlancher = new JLabel ("Plancher");
	/**
	 * Étiquette pour la case de tapis
	 */
	private JLabel lbl_CaseTapis = new JLabel ("Tapis");
	/**
	 * Étiquette pour la case de prise électrique
	 */
	private JLabel lbl_CasePrise = new JLabel ("Prise electrique");
	/**
	 * Étiquette pour la case interdite
	 */
	private JLabel lbl_CaseObstacle = new JLabel ("Obstacle");
	/**
	 * Étiquette pour la séparation entre la légende des cases et celle des robots
	 */
	private JLabel lbl_Separation = new JLabel ("---------------");
	/**
	 * Étiquette pour le robot aléatoire
	 */
	private JLabel lbl_RobotAleatoire = new JLabel ("Robot aleatoire");
	/**
	 * Étiquette pour le robot vorace
	 */
	private JLabel lbl_RobotVorace = new JLabel ("Robot vorace");
	/**
	 * Étiquette pour le robot intelligent
	 */
	private JLabel lbl_RobotIntelligent = new JLabel ("Robot inteligent");
	
	
	/**
	 * Le panneau où apparaîtra l'environnement (la grille)
	 */
	private PanneauEnvironnement m_panneauEnvironnement;
	
	/**
	 * Le minuteur régissant le mouvement des robots
	 */
	private Timer m_Chrono;
	
	/**
	 * Le nom de l'environnement téléchargé
	 */
	private String m_NomEnvironnement;
	
	/**
	 * La date où ont été enregistrés les High Scores de l'environnement chargé
	 */
	private String m_DateHighScores;
	
	/**
	 * L'engin régissant la simulation
	 */
	private Engine m_Engine;
	
	
	/**
	 * Retourne le chrono.
	 * @return le chrono
	 */
	public Timer getChrono()
	{
		return this.m_Chrono;
	}

	/**
	 * Pour modifier le chrono.
	 * @param p_chrono Nouvelle valeur.
	 */
	private void setChrono(Timer p_chrono)
	{
		this.m_Chrono = p_chrono;
	}

	/**
	 * Retourne le nomEnvironnement.
	 * @return le nomEnvironnement
	 */
	public String getNomEnvironnement()
	{
		return this.m_NomEnvironnement;
	}

	/**
	 * Retourne le engine.
	 * @return le engine
	 */
	public Engine getEngine()
	{
		return this.m_Engine;
	}

	/**
	 * Pour modifier le engine.
	 * @param p_engine Nouvelle valeur.
	 */
	private void setEngine(Engine p_engine)
	{
		this.m_Engine = p_engine;
	}

	/**
	 * Retourne le panneauEnvironnement.
	 * @return le panneauEnvironnement
	 */
	public PanneauEnvironnement getPanneauEnvironnement()
	{
		return this.m_panneauEnvironnement;
	}

	/**
	 * Pour modifier le panneauEnvironnement.
	 * @param p_panneauEnvironnement Nouvelle valeur.
	 */
	private void setPanneauEnvironnement(PanneauEnvironnement p_panneauEnvironnement)
	{
		this.m_panneauEnvironnement = p_panneauEnvironnement;
	}
	
	/**
	 * Constructeur par défaut
	 */
	public IntPrincipale()
	{
		super ("Simulation");		

		
		this.setPanneauEnvironnement (new PanneauEnvironnement());
		Container c = this.getContentPane();
		
		this.setResizable(false);
		c.setLayout(new BorderLayout());
		
		//Menu principal
		JMenuBar menuBar = new JMenuBar();
		
		//Ajout du menu "Fichier" avec les items "Charger un environnement" et "Quitter"
		JMenu menuFichier = new JMenu ("Fichier");
		menuFichier.add(this.itm_Charger);
		menuFichier.add(this.itm_Quitter);	
		menuBar.add (menuFichier);
		c.add(menuBar, BorderLayout.NORTH);
		
		//Ajout de l'étiquette pour l'affichage des erreurs reliées à la base de données
		JPanel panneauCentre = new JPanel();
		panneauCentre.setLayout(new BorderLayout());
		this.lbl_ErreurSQL.setForeground(Color.RED);
		panneauCentre.add(this.lbl_ErreurSQL, BorderLayout.NORTH);
		panneauCentre.add(this.m_panneauEnvironnement, BorderLayout.CENTER);
		
//		this.m_panneauEnvironnement.revalidate();
//		this.m_panneauEnvironnement.repaint();
		c.add(panneauCentre, BorderLayout.CENTER);
		
		//Dimensionnement des boutons apparaissant en bas de l'interface
		JPanel panneauBas = new JPanel();
		panneauBas.setLayout(new FlowLayout());
		this.btn_Play.setMinimumSize(new Dimension (120, 40));
		this.btn_Play.setMaximumSize(new Dimension (120, 40));
		this.btn_Play.setPreferredSize(new Dimension (120, 40));
		this.btn_Pause.setMinimumSize(new Dimension (120, 40));
		this.btn_Pause.setMaximumSize(new Dimension (120, 40));
		this.btn_Pause.setPreferredSize(new Dimension (120, 40));
		this.btn_Ralentir.setMinimumSize(new Dimension (120, 40));
		this.btn_Ralentir.setMaximumSize(new Dimension (120, 40));
		this.btn_Ralentir.setPreferredSize(new Dimension (120, 40));
		this.btn_Accelerer.setMinimumSize(new Dimension (120, 40));
		this.btn_Accelerer.setMaximumSize(new Dimension (120, 40));
		this.btn_Accelerer.setPreferredSize(new Dimension (120, 40));
		this.btn_Quitter.setMinimumSize(new Dimension (200, 40));
		this.btn_Quitter.setMaximumSize(new Dimension (200, 40));
		this.btn_Quitter.setPreferredSize(new Dimension (200, 40));		
			
		//Ajout de ces boutons 
		panneauBas.add (this.btn_Play);
		panneauBas.add (this.btn_Pause);
		panneauBas.add (this.btn_Ralentir);
		panneauBas.add (this.btn_Accelerer);
		panneauBas.add (this.btn_Quitter);		
		c.add(panneauBas, BorderLayout.SOUTH);
		
		this.btn_Play.setEnabled(false);
		this.btn_Pause.setEnabled(false);
		this.btn_Ralentir.setEnabled(false);
		this.btn_Accelerer.setEnabled(false);
		
		//Ajout des éléments apparaissant à droite de l'interface
		JPanel panneauDroite = new JPanel();		
		panneauDroite.setLayout(new BorderLayout());
		panneauDroite.setLayout(new GridLayout(12, 1));
		
		panneauDroite.add(this.btn_Charger);
		panneauDroite.add(this.btn_AjoutRobot);
		panneauDroite.add(this.btn_MemoireInterne);
		panneauDroite.add(this.btn_MeilleursPoint);
		
		
		panneauDroite.add(this.lbl_CasePlancher);
		panneauDroite.add(this.lbl_CaseTapis);
		panneauDroite.add(this.lbl_CasePrise);
		panneauDroite.add(this.lbl_CaseObstacle);
		
		panneauDroite.add(this.lbl_Separation);
			
		panneauDroite.add(this.lbl_RobotAleatoire);
		panneauDroite.add(this.lbl_RobotVorace);
		panneauDroite.add(this.lbl_RobotIntelligent);
		
		this.btn_AjoutRobot.setEnabled(false);
		this.btn_MemoireInterne.setEnabled(false);
		this.btn_MeilleursPoint.setEnabled(false);
		c.add(panneauDroite, BorderLayout.EAST);
			
		this.setSize(1000, 800);
		this.setLocationRelativeTo(null);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		//Les événements possibles...
		GestEvenements gestEvenements = new GestEvenements();
		this.itm_Quitter.addActionListener(gestEvenements);
		this.itm_Charger.addActionListener(gestEvenements);
		this.btn_Charger.addActionListener(gestEvenements);
		this.btn_MemoireInterne.addActionListener(gestEvenements);
		this.btn_AjoutRobot.addActionListener(gestEvenements);
		this.btn_Accelerer.addActionListener(gestEvenements);
		this.btn_Ralentir.addActionListener(gestEvenements);
		this.btn_Quitter.addActionListener(gestEvenements);
		this.btn_Play.addActionListener(gestEvenements);
		this.btn_Pause.addActionListener(gestEvenements);		
		this.btn_MeilleursPoint.addActionListener(gestEvenements);

		
		//Initialisation du chronomètre à 2 secondes.  
		IntPrincipale.this.setChrono(new Timer (2000, new ActionListener()
		{
			public void actionPerformed(ActionEvent p_e)
			{
				IntPrincipale.this.m_panneauEnvironnement.getPanneauDessin().faireMouvement();
			}
		}));	
	}
	
	/**
	 * Gestionnaire des événements de l'interface principale (simulation).
	 * 
	 * @author Jean-François Bédard, Marie-Pier Chabot, Alexandre Paquet Fasys, Claude Roy
	 */
	private class GestEvenements implements ActionListener
	{	
		@Override
		public void actionPerformed(ActionEvent p_e)
		{
			
			//pour l'item "Charger un environnement" du menu "Fichier" ou 
			//pour le bouton "Charger un environnement"
			if ((p_e.getSource() == IntPrincipale.this.itm_Charger) ||
					(p_e.getSource() == IntPrincipale.this.btn_Charger))
			{
				// On enregistre les récompenses associées à l'environnement s'il y en a déjà un de chargé
				// avant d'en charger un autre
				if (IntPrincipale.this.m_NomEnvironnement != null){
					
					compareAndSaveRecompenses();
				}
				
				File nomFichier = null;
				JFileChooser chooser = new JFileChooser();

				chooser.setCurrentDirectory(new File ("F:\\Progobjet\\TP2-2\\src\\res"));


				int resultat = chooser.showOpenDialog(IntPrincipale.this);
				if (resultat == JFileChooser.APPROVE_OPTION)
				{
					nomFichier = chooser.getSelectedFile();
				}
				
				try
				{
					String st = nomFichier.toString();
					IntPrincipale.this.m_NomEnvironnement = st;
					Random randomGenerateur = new Random();
					Engine engin = new Engine(st, randomGenerateur);
					IntPrincipale.this.setEngine(engin);
					IntPrincipale.this.getPanneauEnvironnement().setEngine(engin);
					
					//fait appel à la méthode "repaint"
					IntPrincipale.this.getPanneauEnvironnement().getPanneauDessin().getSignal().setData();	
					IntPrincipale.this.btn_AjoutRobot.setEnabled(true);
					IntPrincipale.this.btn_MeilleursPoint.setEnabled(true);
					IntPrincipale.this.btn_MemoireInterne.setEnabled(false);
					IntPrincipale.this.btn_Play.setEnabled(false);
					
					
				}
				catch (FileNotFoundException exp)
				{
					JOptionPane.showMessageDialog(null, "Le fichier est inexistant.", "Erreur", JOptionPane.ERROR_MESSAGE);
				}
				catch (XMLStreamException exp)
				{
					JOptionPane.showMessageDialog(null, "Le fichier ne peut pas etre charge.", "Erreur", JOptionPane.ERROR_MESSAGE);
				}	
				catch (Exception exp)
				{
					JOptionPane.showMessageDialog(null, "Erreur dans la construction de l'engin, non reliée à " + 
							" l'inexistence du fichier demandé ou à la lecture de ce fichier.", "Erreur", JOptionPane.ERROR_MESSAGE);
				}
			}
			
			//pour l'item "Quitter" du menu "Fichier" ou 
			//pour le bouton "Retour à la page d'accueil"
			else if ((p_e.getSource() == IntPrincipale.this.itm_Quitter) ||
					(p_e.getSource() == IntPrincipale.this.btn_Quitter))
			{
				IntPageAcceuil maPageAccueil = new IntPageAcceuil();
				maPageAccueil.setVisible(true);
		    	
				compareAndSaveRecompenses();
				
				IntPrincipale.this.dispose();
			}
			
			//pour le bouton "Ajouter un/des robot(s)"
			else if (p_e.getSource() == IntPrincipale.this.btn_AjoutRobot)
			{
				AjouterRobot maPageAjoutRobot = new AjouterRobot(IntPrincipale.this, IntPrincipale.this.getEngine());
				maPageAjoutRobot.setVisible(true);
				
				//On établit l'engin avec les robots sélectionnés
				IntPrincipale.this.getPanneauEnvironnement().setEngine(IntPrincipale.this.getEngine());
				
				// Si on a atteint 20 robots, on désactive le bouton pour ne plus en ajouter davantage
				if(IntPrincipale.this.getEngine().getListRobots().size() == 20){
					
					IntPrincipale.this.btn_AjoutRobot.setEnabled(false);
				}
				
				//On place au hasard les robots sur la grille
				for (Robot robot : IntPrincipale.this.getEngine().getListRobots())
				{
					Random randomGenerator = new Random();
					//Pour ne pas replacer des robots déjà positionnés, au cas où fait un deuxième ou
					//troisième ajout de robots
					if (robot.getPosition() == null)
					{
						Position position = IntPrincipale.this.getEngine().getEnvironnement().getNewPos(randomGenerator);
						robot.setPosition(position);
					}
				}

				IntPrincipale.this.btn_Play.setEnabled(true);			
				IntPrincipale.this.getPanneauEnvironnement().getPanneauDessin().getSignal().setData();
			}
			
			//pour le bouton "Mémoire interne"
			else if (p_e.getSource() == IntPrincipale.this.btn_MemoireInterne)
			{
				IntMemoireInterne maPageMemoireInt = new IntMemoireInterne(IntPrincipale.this.getEngine());
				maPageMemoireInt.setVisible(true);	
			}
			
			//pour le bouton "Afficher les meilleurs pointages"
			else if (p_e.getSource() == IntPrincipale.this.btn_MeilleursPoint)
			{
				int[] tabRecompenses = compareAndSaveRecompenses();
				
				IntHighScores JDHighScore = new IntHighScores(tabRecompenses, 
											IntPrincipale.this.m_NomEnvironnement, IntPrincipale.this.m_DateHighScores);
				JDHighScore.setVisible(true);
			}
			
			//pour le bouton "Play"
			else if (p_e.getSource() == IntPrincipale.this.btn_Play)
			{			
				IntPrincipale.this.btn_Pause.setEnabled(true);
				IntPrincipale.this.btn_Play.setEnabled(false);
				if (IntPrincipale.this.getChrono().getDelay() > 400){
					IntPrincipale.this.btn_Accelerer.setEnabled(true);
				}
				IntPrincipale.this.btn_Ralentir.setEnabled(true);
				IntPrincipale.this.btn_AjoutRobot.setEnabled(false);
				IntPrincipale.this.btn_Charger.setEnabled(false);
				IntPrincipale.this.itm_Charger.setEnabled(false);
				IntPrincipale.this.btn_MemoireInterne.setEnabled(false);
				IntPrincipale.this.getChrono().start();
			}
			
			//pour le bouton "Pause"
			else if (p_e.getSource() == IntPrincipale.this.btn_Pause)
			{
				IntPrincipale.this.btn_Play.setEnabled(true);
				IntPrincipale.this.btn_Pause.setEnabled(false);
				IntPrincipale.this.btn_Accelerer.setEnabled(false);
				IntPrincipale.this.btn_Ralentir.setEnabled(false);
				IntPrincipale.this.btn_Charger.setEnabled(true);
				IntPrincipale.this.itm_Charger.setEnabled(true);
				IntPrincipale.this.btn_MemoireInterne.setEnabled(false);
				if (IntPrincipale.this.getEngine().getListRobotsIntelligents().size()!=0)
				{
					IntPrincipale.this.btn_MemoireInterne.setEnabled(true);
				}
				IntPrincipale.this.getChrono().stop();
			}
			
			//pour le bouton "Ralentir" (augmentation de 0.5 seconde pour les intervalles du chronomètre)
			else if (p_e.getSource() == IntPrincipale.this.btn_Ralentir)
			{
				IntPrincipale.this.getChrono().setDelay(IntPrincipale.this.getChrono().getDelay() + 500);
				IntPrincipale.this.btn_Accelerer.setEnabled(true);
			}
			
			//pour le bouton "Accélérer" (diminution de 0.5 seconde pour les intervalles du chronomètre)
			else if (p_e.getSource() == IntPrincipale.this.btn_Accelerer)
			{
				IntPrincipale.this.getChrono().setDelay(IntPrincipale.this.getChrono().getDelay() - 500);
				
				//l'accélération ne sera plus possible si les intervalles du chronomètre
				//sont inférieurs à 0.5 seconde.  
				if (IntPrincipale.this.getChrono().getDelay() <= 400)
				{
					IntPrincipale.this.btn_Accelerer.setEnabled(false);
				}
			}		
		}
	}
	
	
	/**
	 * Méthode pour comparer les valeurs des scores des robots actuellement dans un environnement
	 * à ceux conservés en BD. Si les scores actuels sont meilleurs, ont les remplace dans la BD
	 * pour l'environnement en question et on remplace la date de la simulation pour celle du jour.
	 * @return Un tableau contenant les meilleurs scores, dans l'ordre: robot intelligent,
	 * 			robot geedy, robot random
	 */
	private int[] compareAndSaveRecompenses(){
		
		int[] tabRecompenses = new int[3];
		boolean changement = false;
		String date = "ND";
		
		try
		{
			DbRecompenses Db = DbRecompenses.getInstance();
			
			String nomEnv = IntPrincipale.this.m_NomEnvironnement;
			
			// On va chercher les récompenses présentement enregistrées pour cette environnement
			tabRecompenses = Db.getRecompenses(nomEnv);
			date = Db.getDate(nomEnv);
			
			// Si on a ajouté des robots dans l'environnement, on compare aux valeurs
			// présentement dans la BD pour cette environnement et on conserve les meilleurs résultats
			if (IntPrincipale.this.getEngine().getListRobots().size() > 0)
			{
				for (Robot bot : IntPrincipale.this.getEngine().getListRobots()){
					
					if(bot instanceof RobotIntelligent){
						
						if(bot.getAccReward() >= tabRecompenses[0]){
							
							tabRecompenses[0] = bot.getAccReward();
							changement = true;
						}
					}
					
					if(bot instanceof RobotGreedy){
						
						if(bot.getAccReward() >= tabRecompenses[1]){
							
							tabRecompenses[1] = bot.getAccReward();
							changement = true;
						}
					}
					if(bot instanceof RobotRandom){
						
						if(bot.getAccReward() >= tabRecompenses[2]){
							
							tabRecompenses[2] = bot.getAccReward();
							changement = true;
						}
					}
				}
				// S'il y au du changement, on remplace la date pour celle du jour
				// puis on enregistre
				if(changement){
					
					Date today = new Date();
					DateFormat formatDate = new SimpleDateFormat("dd/MMM/yyyy");
					date = formatDate.format(today).toString();
					Db.saveRecompensesDate(nomEnv, tabRecompenses, date);
				}
			}
		}
		catch(SQLException e)
		{
			IntPrincipale.this.lbl_ErreurSQL.setText("Erreur SQL : " + e.getMessage());
		}
		catch(ClassNotFoundException e)
		{
			IntPrincipale.this.lbl_ErreurSQL.setText("Le driver SqliteJDBC n'est pas dans le classpath.");
		}
		catch (Exception e)
		{
			
		}
		
		this.m_DateHighScores = date;
		return tabRecompenses;
	}
}
	
	

